package com.example.opencvdemo

import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.util.Log
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import org.opencv.android.Utils
import org.opencv.core.Core
import org.opencv.core.Mat
import org.opencv.core.Point
import org.opencv.core.Rect
import org.opencv.core.Scalar
import org.opencv.imgproc.Imgproc
import kotlin.math.abs

private const val TAG = "MyViewModel"

class MyViewModel : ViewModel() {
    private val _recognizedBitmap =
        MutableStateFlow(Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888))
    val recognizedBitmap = _recognizedBitmap.asStateFlow()

    private val _dataList = MutableStateFlow(listOf(0, 0, 0, 0))
    val dataList = _dataList.asStateFlow()

    // 图片识别,在协程中进行图片识别
    fun recognizeImage(context: Context, originalUri: Uri) {
        // 协程
        CoroutineScope(Dispatchers.IO).launch {
            val dst = Mat()
            val targetImages: MutableList<Mat> = ArrayList()
            targetImages.add(Utils.loadResource(context, R.drawable.ceshi1))
            targetImages.add(Utils.loadResource(context, R.drawable.ceshi2))
            targetImages.add(Utils.loadResource(context, R.drawable.ceshi3))
            targetImages.add(Utils.loadResource(context, R.drawable.ceshi4))
            val input = context.contentResolver.openInputStream(originalUri)
            val bitmap = BitmapFactory.decodeStream(input)
            val originalImage = Mat()
            Utils.bitmapToMat(bitmap, originalImage)

//            Log.e(TAG, "recognizeImage: ${originalImage.type()}")
            Imgproc.cvtColor(originalImage, originalImage, Imgproc.COLOR_RGBA2BGR)
            val out = matchTemplateAndFindDigits(context, dst, originalImage, targetImages)
            _dataList.value = out
            val dstBitmap = Bitmap.createBitmap(dst.cols(), dst.rows(), Bitmap.Config.ARGB_8888)
            //转换回RGB颜色
            Imgproc.cvtColor(dst, dst, Imgproc.COLOR_BGR2RGB)
            Utils.matToBitmap(dst, dstBitmap)
            _recognizedBitmap.value = dstBitmap
            Log.e(TAG, "recognizeImage: out!!")
        }
    }
}

/**
 * 图片匹配识别，识别指定图片下面数字，并返回List
 */
fun matchTemplateAndFindDigits(
    context: Context,
    dst: Mat,
    originalImage: Mat,
    targetImages: List<Mat>
): List<Int> {
//    val originalImg: Mat
    val out = mutableListOf<Int>()
//    try {
    // 读取图片
//        originalImg = Utils.loadResource(context, R.drawable.y20240528111908)
//    } catch (e: Exception) {
//        e.printStackTrace()
//        return listOf()
//    }

    // 加载数字模板
    val templates: MutableList<Mat> = ArrayList()
    templates.add(Utils.loadResource(context, R.drawable.n0))
    templates.add(Utils.loadResource(context, R.drawable.n1))
    templates.add(Utils.loadResource(context, R.drawable.n2))
    templates.add(Utils.loadResource(context, R.drawable.n3))
    templates.add(Utils.loadResource(context, R.drawable.n4))
    templates.add(Utils.loadResource(context, R.drawable.n5))
    templates.add(Utils.loadResource(context, R.drawable.n6))
    templates.add(Utils.loadResource(context, R.drawable.n7))
    templates.add(Utils.loadResource(context, R.drawable.n8))
    templates.add(Utils.loadResource(context, R.drawable.n9))

    for (targetImage in targetImages) {
        // 获取目标图片宽高
        val targetImgWidth = targetImage.width()
        val targetImgHeight = targetImage.height()
        // 使用模板匹配方法
        val res = Mat()
        Imgproc.matchTemplate(originalImage, targetImage, res, Imgproc.TM_CCOEFF_NORMED)

        // 找到最佳匹配位置
        val minMaxLocResult = Core.minMaxLoc(res)
        val maxVal = minMaxLocResult.maxVal
        val maxLoc = minMaxLocResult.maxLoc
        if (maxVal < 0.8) {
            Log.e(TAG, "The target image was not found in the picture!")
            out.add(-1)
            continue
        }

        // 绘制矩形框
        Imgproc.rectangle(
            originalImage,
            Rect(maxLoc.x.toInt(), maxLoc.y.toInt(), targetImgWidth, targetImgHeight),
            Scalar(0.0, 0.0, 255.0),
            8
        )
        // 记录右下角坐标
        val bottomRight =
            Point(maxLoc.x + targetImgWidth, maxLoc.y + targetImgHeight)
        // 输出矩形四点坐标
        Log.d(
            TAG,
            "HomeworkCompose: Rectangle coordinates: ${maxLoc.x}, ${maxLoc.y} ${maxLoc.x + targetImgWidth} ${maxLoc.y + targetImgHeight}"
        )

        // 定义搜索区域
        val searchRect =
            Rect(maxLoc.x.toInt(), bottomRight.y.toInt(), targetImgWidth, targetImgHeight / 2)
        Imgproc.rectangle(originalImage, searchRect, Scalar(0.0, 255.0, 255.0), 8)
        val searchArea = Mat(originalImage, searchRect)

        val num = findDigits(searchArea, templates)
        out.add(num)
    }

    originalImage.copyTo(dst)
    Log.e(TAG, "matchTemplateAndFindDigits: out")
    return out
}


/**
 * 在搜索区域内查找数字
 * @param searchArea 搜索区域Mat
 * @param templates 数字模板List
 * @return 匹配到的数字
 */
fun findDigits(searchArea: Mat, templates: List<Mat>): Int {
    Log.e(TAG, "FindDigits: searchArea size: ${searchArea.size()}")
    val digitMap = mutableMapOf<Int, Int>()
    var out = -1

    // 对每个模板进行匹配
    for ((index, template) in templates.withIndex()) {
        val result = Mat()
        Imgproc.matchTemplate(searchArea, template, result, Imgproc.TM_CCOEFF_NORMED)
        Log.e(TAG, "FindDigits: $index --------------------")

        // 找到最佳匹配位置
        val minMaxLocResult = Core.minMaxLoc(result)
        val maxLoc = minMaxLocResult.maxLoc
        val maxVal = minMaxLocResult.maxVal
        Log.e(TAG, "maxVal: $maxVal maxLoc: $maxLoc")
        if (maxVal > 0.8) {
            // 遍历结果矩阵，查找匹配的数字
            for (j in 0..<result.height()) {
                for (k in 0..<result.width()) {
                    if (result.at(Float::class.java, j, k).v > 0.8) {
                        Log.e(TAG, "FindDigits: ${result.at(Float::class.java, j, k).v} ($k,$j)")
                        var exist = false
                        for ((key, value) in digitMap) {
                            Log.e(TAG, "key: $key, value: $value")
                            if (abs(key - k) < 10) {
                                exist = true
                                break
                            }
                        }
                        if (!exist) {
                            digitMap[k] = index
                            Log.e(TAG, "findDigits: insert key: $k value: $index")
                        }
                    }
                }
            }
            val sortedMap = digitMap.toSortedMap()
            out = 0
            for (entry in sortedMap.entries) {
                out = out * 10 + entry.value
            }
            Log.e(TAG, "findDigits: $out")
        }
    }
    return out
}
